Files for my website
bwc9876.dev
1---
2import { type CollectionEntry, getCollection, render } from "astro:content";
3import { Image } from "astro:assets";
4import Layout from "@layouts/Layout.astro";
5import IconLink from "@components/IconLink.astro";
6import { Icon } from "astro-icon/components";
7export async function getStaticPaths() {
8 const renameEntries = await getCollection("projects");
9 return renameEntries.map((entry) => ({
10 params: { slug: entry.id },
11 props: { entry }
12 }));
13}
14const { entry } = Astro.props as { entry: CollectionEntry<"projects"> };
15const { Content } = await render(entry);
16
17const firstOtherLink = entry.data.links?.other?.[0];
18
19const restOther = entry.data.links?.other?.slice(1);
20
21const og = {
22 src: entry.data.image,
23 alt: entry.data.name
24};
25---
26
27<Layout title={entry.data.name} description={entry.data.summary} keywords={entry.data.tags} og={og}>
28 <div class="hero">
29 <div class="txt">
30 <div class="project-header">
31 <h1 class="gradient-text">{entry.data.name}</h1>
32 <p>
33 {entry.data.timespan.from}{entry.data.timespan.to && <> - {entry.data.timespan.to}</>},
34 {entry.data.tags.join(", ")}
35 </p>
36 </div>
37 <p>{entry.data.summary}</p>
38 <div class="ctas">
39 {
40 entry.data.links?.github && (
41 <span>
42 <a href={`https://github.com/${entry.data.links.github}`} role="button">
43 <Icon name="bi:github" /> GitHub
44 </a>
45 </span>
46 )
47 }
48 {
49 firstOtherLink && (
50 <span>
51 <a href={firstOtherLink.url} role="button" class="secondary">
52 <Icon name={`${firstOtherLink.iconPackOverride ?? "bi"}:${firstOtherLink.icon}`} />
53 {firstOtherLink.label}
54 </a>
55 </span>
56 )
57 }
58 </div>
59 <div class="links">
60 {
61 restOther &&
62 restOther.map((l) => (
63 <span>
64 <IconLink
65 icon={l.icon}
66 overridePack={l.iconPackOverride}
67 href={l.url}
68 placement="bottom"
69 label={l.label}
70 />
71 </span>
72 ))
73 }
74 </div>
75 </div>
76 <div class="img-container">
77 <Image
78 transition:name={`project-img-${entry.id}`}
79 alt={entry.data.name}
80 src={entry.data.image}
81 />
82 </div>
83 </div>
84 <h2>About This Project</h2>
85 <Content />
86</Layout>
87
88<style>
89 div.project-header h1 {
90 margin-bottom: var(--1);
91 }
92
93 div.project-header p {
94 font-size: var(--2);
95 margin: 0;
96 }
97
98 .hero {
99 display: flex;
100 gap: var(--5);
101 justify-content: space-between;
102 align-items: center;
103 margin: var(--4) 0;
104 }
105
106 @media (width <= 1300px) {
107 .hero {
108 flex-direction: column-reverse;
109 margin-top: var(--1);
110 gap: var(--2);
111 text-align: center;
112 }
113
114 .links,
115 .ctas {
116 margin: auto;
117 }
118
119 .hero p {
120 margin: 0;
121 }
122
123 div.project-header {
124 h1 {
125 margin-top: 0;
126 }
127 p {
128 text-wrap: balance;
129 }
130 }
131 }
132
133 .hero .txt {
134 width: 100%;
135 flex-grow: 1;
136 display: flex;
137 flex-direction: column;
138 gap: var(--2);
139 }
140
141 .hero .ctas {
142 display: flex;
143 align-items: center;
144 gap: var(--2);
145 }
146
147 .hero .ctas a[role="button"] {
148 display: flex;
149 align-items: center;
150 gap: var(--small);
151 }
152
153 .hero .img-container {
154 flex-grow: 1;
155 width: 100%;
156 justify-content: end;
157 align-items: center;
158 display: flex;
159 margin: auto;
160 }
161
162 .hero .img-container img {
163 border-radius: 5px;
164 filter: drop-shadow(0 0 100px color(from var(--primary) srgb r g b / 0.2));
165 width: 60%;
166 height: auto;
167 margin: auto;
168 }
169
170 .links {
171 display: flex;
172 gap: var(--2);
173 }
174</style>